package com.effectiv.gooruda.rest.service.api;

import java.util.List;

import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;

import com.effectiv.gooruda.rest.docs.annotation.Api;
import com.effectiv.gooruda.rest.docs.annotation.ApiBodyObject;
import com.effectiv.gooruda.rest.docs.annotation.ApiHeader;
import com.effectiv.gooruda.rest.docs.annotation.ApiHeaders;
import com.effectiv.gooruda.rest.docs.annotation.ApiMethod;
import com.effectiv.gooruda.rest.docs.annotation.ApiParam;
import com.effectiv.gooruda.rest.docs.annotation.ApiResponseObject;
import com.effectiv.gooruda.rest.docs.pojo.ApiVerb;
import com.effectiv.gooruda.rest.domain.Profile;
import com.effectiv.gooruda.rest.domain.Role;
import com.effectiv.gooruda.rest.domain.User;
import com.effectiv.gooruda.rest.ext.ExtResponse;
import com.effectiv.gooruda.rest.ext.SecurityContextHolder;
@Path("/user")
@Api(name = "User API", description = "User management API.")
public abstract class UserService {
	
	private final String COLLECTION_BASE = "c_app_user_";
	
	private final String SALT = "gooruda-2";

	@Getter @Setter
	@Autowired
	private MongoTemplate template;

	protected String getCollection(){
		return COLLECTION_BASE + SecurityContextHolder.get().getId();
	}
	
	protected String getSalt(){
		return SALT;
	}
	protected void throwException(int errorCode, String errorMessage){
		ExtResponse extRes = new ExtResponse();
		extRes.setErrorCode(errorCode);
		extRes.setMessage(errorMessage);
		
		ResponseBuilder resBuilder = Response.status(Status.BAD_REQUEST);
		resBuilder.entity(extRes);
		
		throw new WebApplicationException(resBuilder.build());
	}
	
	/**
	 * Save user details
	 * @param user
	 * @return
	 */
	
	@ApiMethod(path = "/api/user", verb = ApiVerb.POST, description = "Saves user details.", 
			produces = { MediaType.APPLICATION_JSON }, consumes = { MediaType.APPLICATION_JSON })
	@ApiHeaders(headers = {
			@ApiHeader(name = "signature", description = "Authentication signature."),
			@ApiHeader(name = "public-key", description = "Public key of the application.") })
	@POST	
	@Produces(MediaType.APPLICATION_JSON)
	public abstract @ApiResponseObject Response save(@ApiBodyObject User user);
	
	
	/**
	 * Add a role to user
	 * @param userName
	 * @param roleName
	 * @return
	 */
	@ApiMethod(path = "/api/user/addrole/{userName}/{roleName}", verb = ApiVerb.POST, description = "Add a role to user.", 
			produces = { MediaType.APPLICATION_JSON }, consumes = { MediaType.APPLICATION_JSON })
	@ApiHeaders(headers = {
			@ApiHeader(name = "signature", description = "Authentication signature."),
			@ApiHeader(name = "public-key", description = "Public key of the application.") })
	
	@Path("/addrole/{userName}/{roleName}")
	@POST
	@Produces(MediaType.APPLICATION_JSON)
	public abstract @ApiResponseObject Response addRole(@PathParam("userName") String userName, @PathParam("roleName") String roleName);

	/**
	 * Add roles to user.
	 * @param userName
	 * @param roleNames
	 * @return
	 */
	
	@ApiMethod(path = "/api/user/addroles/{userName}", verb = ApiVerb.POST, description = "Add a roles to user.", 
			produces = { MediaType.APPLICATION_JSON }, consumes = { MediaType.APPLICATION_JSON })
	@ApiHeaders(headers = {
			@ApiHeader(name = "signature", description = "Authentication signature."),
			@ApiHeader(name = "public-key", description = "Public key of the application.") })
	
	@Path("/addroles/{userName}")
	@POST
	@Produces(MediaType.APPLICATION_JSON)
	public abstract @ApiResponseObject Response addRoles(@PathParam("userName") String userName, @ApiBodyObject List<String> roleNames);

	/**
	 * Authenticate an user.
	 * @param userName
	 * @param password
	 * @return
	 */
	@ApiMethod(path = "/api/user/authenticate/{userName}/{password}", verb = ApiVerb.POST, description = "Add a roles to user.", 
			produces = { MediaType.APPLICATION_JSON }, consumes = { MediaType.APPLICATION_JSON })
	@ApiHeaders(headers = {
			@ApiHeader(name = "signature", description = "Authentication signature."),
			@ApiHeader(name = "public-key", description = "Public key of the application.") })
	
	@Path("/authenticate/{userName}/{password}")
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	public abstract Response authenticate(@PathParam("userName") String userName,@PathParam("password") String password,
			@QueryParam("startSession") @DefaultValue("false") boolean session);

	
	/**
	 * Change password.
	 * @param userName
	 * @param oldPassword
	 * @param newPassword
	 * @return
	 */
	
	@ApiMethod(path = "/api/user/changepassword/{userName}/{oldPassword}/{newPassword}", verb = ApiVerb.POST, 
			description = "Changes user password.", produces = { MediaType.APPLICATION_JSON }, consumes = { MediaType.APPLICATION_JSON })
	@ApiHeaders(headers = {
			@ApiHeader(name = "signature", description = "Authentication signature."),
			@ApiHeader(name = "public-key", description = "Public key of the application.") })	
	@ApiResponseObject
	@Path("/changepassword/{userName}/{oldPassword}/{newPassword}")
	@POST
	@Produces(MediaType.APPLICATION_JSON)
	public abstract Response changePassword(@PathParam("userName") String userName, @PathParam("oldPassword") String oldPassword,
			@PathParam("newPassword") String newPassword);

	/**
	 * Find user details by user name
	 * @param userName
	 * @return
	 */
	
	@ApiMethod(path = "/api/user/findbyusername/{userName}", verb = ApiVerb.GET, 
			description = "Retrieve user details by user name.", produces = { MediaType.APPLICATION_JSON }, consumes = { MediaType.APPLICATION_JSON })
	@ApiHeaders(headers = {
			@ApiHeader(name = "signature", description = "Authentication signature."),
			@ApiHeader(name = "public-key", description = "Public key of the application.") })	
	@ApiResponseObject
	@Path("/findbyusername/{userName}")
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	public abstract Response findByUserName(@PathParam("userName") String userName);
	

	/**
	 * Add or update profile
	 * @param userName
	 * @return
	 */
	
	@ApiMethod(path = "/api/user/upsertprofile/{userName}", verb = ApiVerb.GET, 
			description = "Add or update user profile.", produces = { MediaType.APPLICATION_JSON }, consumes = { MediaType.APPLICATION_JSON })
	@ApiHeaders(headers = {
			@ApiHeader(name = "signature", description = "Authentication signature."),
			@ApiHeader(name = "public-key", description = "Public key of the application.") })	
	@ApiResponseObject
	@Path("/upsertprofile/{userName}")
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	
	public abstract Response upsertProfile(@PathParam("userName") String userName, Profile profile);
	
	/**
	 * Deletes an user
	 * @param userName
	 * @return
	 */
	
	@ApiMethod(path = "/api/user/delete/{userName}", verb = ApiVerb.POST, 
			description = "Add or update user profile.", produces = { MediaType.APPLICATION_JSON }, consumes = { MediaType.APPLICATION_JSON })
	@ApiHeaders(headers = {
			@ApiHeader(name = "signature", description = "Authentication signature."),
			@ApiHeader(name = "public-key", description = "Public key of the application.") })	
	
	@ApiResponseObject
	@Path("/delete/{userName}")
	@POST
	@Produces(MediaType.APPLICATION_JSON)
	public abstract Response deleteUser(String userName);
	
	
	/**
	 * Find all users for a given application
	 * @param pageNo
	 * @param pageSize
	 * @return
	 */
	
	@ApiMethod(path = "/api/user/findall", verb = ApiVerb.POST, 
			description = "Find all users for a given application.", produces = { MediaType.APPLICATION_JSON }, consumes = { MediaType.APPLICATION_JSON })
	@ApiHeaders(headers = {
			@ApiHeader(name = "signature", description = "Authentication signature."),
			@ApiHeader(name = "public-key", description = "Public key of the application.") })	
	
	@ApiResponseObject
	@Path("/findall")
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	public abstract List<User> findAll(int pageNo, int pageSize);
	
	/**
	 * Returns the count of all the users for an application.
	 * @return
	 */
	
	@ApiMethod(path = "/api/user/count", verb = ApiVerb.POST, 
			description = "Find all users for a given application.", produces = { MediaType.APPLICATION_JSON }, consumes = { MediaType.APPLICATION_JSON })
	@ApiHeaders(headers = {
			@ApiHeader(name = "signature", description = "Authentication signature."),
			@ApiHeader(name = "public-key", description = "Public key of the application.") })	
	
	@ApiResponseObject
	@Path("/count")
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	public abstract long count();
	
	/**
	 * Find all the locked users. 
	 * @param pageNo
	 * @param pageSize
	 * @return
	 */
	
	@ApiMethod(path = "/api/user/findlockedusers", verb = ApiVerb.POST, 
			description = "Find all locked users for a given application.", produces = { MediaType.APPLICATION_JSON }, consumes = { MediaType.APPLICATION_JSON })
	@ApiHeaders(headers = {
			@ApiHeader(name = "signature", description = "Authentication signature."),
			@ApiHeader(name = "public-key", description = "Public key of the application.") })	
	
	@ApiResponseObject
	@Path("/findlockedusers")
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	public abstract List<User> findLockedUsers(int pageNo, int pageSize);
	
	/**
	 * Count locked users
	 * @return
	 */
	
	@ApiMethod(path = "/api/user/countlocked", verb = ApiVerb.POST, 
			description = "Count all locked users for a given application.", produces = { MediaType.APPLICATION_JSON }, consumes = { MediaType.APPLICATION_JSON })
	@ApiHeaders(headers = {
			@ApiHeader(name = "signature", description = "Authentication signature."),
			@ApiHeader(name = "public-key", description = "Public key of the application.") })	
	
	@ApiResponseObject
	@Path("/countlocked")
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	public abstract long countLockedUsers();
	
	/**
	 * Find all the roles for a given user.
	 * @param userName
	 * @return
	 */
	
	@ApiMethod(path = "/api/user/findrolesforuser", verb = ApiVerb.POST, 
			description = "Find all the roles for a given user.", produces = { MediaType.APPLICATION_JSON }, consumes = { MediaType.APPLICATION_JSON })
	@ApiHeaders(headers = {
			@ApiHeader(name = "signature", description = "Authentication signature."),
			@ApiHeader(name = "public-key", description = "Public key of the application.") })	
	
	@ApiResponseObject
	@Path("/findrolesforuser")
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	public abstract List<Role> findRolesForUser(String userName);
	
	/**
	 * Find an user by id
	 * @param id
	 * @return
	 */
	@ApiMethod(path = "/api/user/findone", verb = ApiVerb.POST, 
			description = "Find an user by id.", produces = { MediaType.APPLICATION_JSON }, consumes = { MediaType.APPLICATION_JSON })
	@ApiHeaders(headers = {
			@ApiHeader(name = "signature", description = "Authentication signature."),
			@ApiHeader(name = "public-key", description = "Public key of the application.") })	
	
	@ApiResponseObject
	@Path("/findone")
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	public abstract User findOne(String id);
	
	/**
	 * Find an user by email
	 * @param email
	 * @return
	 */
	@ApiMethod(path = "/api/user/findbyemail/{email}", verb = ApiVerb.POST, 
			description = "Find an user by email.", produces = { MediaType.APPLICATION_JSON }, consumes = { MediaType.APPLICATION_JSON })
	@ApiHeaders(headers = {
			@ApiHeader(name = "signature", description = "Authentication signature."),
			@ApiHeader(name = "public-key", description = "Public key of the application.") })	
	
	@ApiResponseObject
	@Path("/findbyemail/{email}")
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	public abstract User findByEmail(String email);
	
	/**
	 * Lock an user account
	 * @param userName
	 * @return
	 */
	@ApiMethod(path = "/api/user/lock/{userName}", verb = ApiVerb.POST, 
			description = "Lock an user account", produces = { MediaType.APPLICATION_JSON }, consumes = { MediaType.APPLICATION_JSON })
	@ApiHeaders(headers = {
			@ApiHeader(name = "signature", description = "Authentication signature."),
			@ApiHeader(name = "public-key", description = "Public key of the application.") })	
	
	@ApiResponseObject
	@Path("/lock/{userName}")
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	public abstract User lock(String userName);
	
	
	/**
	 * Unlock an user account
	 * @param userName
	 * @return
	 */
	@ApiMethod(path = "/api/user/unlock/{userName}", verb = ApiVerb.POST, 
			description = "Lock an user account", produces = { MediaType.APPLICATION_JSON }, consumes = { MediaType.APPLICATION_JSON })
	@ApiHeaders(headers = {
			@ApiHeader(name = "signature", description = "Authentication signature."),
			@ApiHeader(name = "public-key", description = "Public key of the application.") })	
	
	@ApiResponseObject
	@Path("/unlock/{userName}")
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	public abstract User unlock(String userName);
	
	/**
	 * Remove all roles for an user.
	 * @param userName
	 * @return
	 */
	
	@ApiMethod(path = "/api/user/removeallroles/{userName}", verb = ApiVerb.POST, 
			description = "Remove all roles for an user.", produces = { MediaType.APPLICATION_JSON }, consumes = { MediaType.APPLICATION_JSON })
	@ApiHeaders(headers = {
			@ApiHeader(name = "signature", description = "Authentication signature."),
			@ApiHeader(name = "public-key", description = "Public key of the application.") })	
	
	@ApiResponseObject
	@Path("/removeallroles/{userName}")
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	public abstract Response removeAllRoles(String userName);
	
	
	/**
	 * Updates an user's email
	 * @param userName
	 * @param newEmail
	 * @return
	 */

	@ApiMethod(path = "/api/user/updateemail/{userName}/{newEmail}", verb = ApiVerb.POST, 
			description = "Updates an user's email.", produces = { MediaType.APPLICATION_JSON }, consumes = { MediaType.APPLICATION_JSON })
	@ApiHeaders(headers = {
			@ApiHeader(name = "signature", description = "Authentication signature."),
			@ApiHeader(name = "public-key", description = "Public key of the application.") })	
	
	@ApiResponseObject
	@Path("/updateemail/{userName}/{newEmail}")
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	public abstract Response updateEmail(@PathParam("userName") 
		@ApiParam(name="userName", description="User name") String userName,@PathParam("newEmail") @ApiParam(name="newEmail", description="New Email.")   String newEmail);
}
